package org.marverse.scripting

import scala.collection.immutable.HashMap
import ScriptingUtil._

/**
 * Defines a member of a type and how to obtain it from a value. 
 */
trait MemberDeclaration extends Function {
  val name: Symbol
  val baseType: Type
  val memberType: Type
  override def toString = "%s.%s".format(baseType.toString, name.name)
}

/**
 * Convenience implementation for members that always returns the same value.
 */
case class ConstMember(val name: Symbol, val baseType: Type, val memberType: Type, val value: Any) 
extends MemberDeclaration {
  override def apply(argument: Any)(implicit ctx: MutableTypedMap[TypedKey, Any]) = value
}

/**
 * Convenience implementation for members that returns a simple value. The member
 * is actually implemented by <code>body</code>.
 */
case class ValueMember(val name: Symbol, val baseType: Type, val memberType: Type, val body: Any => Any) 
extends MemberDeclaration {
  override def apply(argument: Any)(implicit ctx: MutableTypedMap[TypedKey, Any]) = body(argument)
}

/**
 * Convenience implementation for members that return an unary function. The member is 
 * actually implementated by <code>body</code>, a binary function that takes the value and the parameter
 * to the unary function.
 */
case class UnaryMember(val name: Symbol, val baseType: Type, argumentType: Type, resultType: Type, body: (Any, Any) => Any) 
extends MemberDeclaration {
  val memberType = FunctionType(argumentType, resultType)
  override def apply(argument1: Any)(implicit ctx: MutableTypedMap[TypedKey, Any]) = new Function() {
    override def toString = "%s.%s".format(argument1, name.name)
    override def apply(argument2: Any)(implicit ctx2: MutableTypedMap[TypedKey, Any]) =
      body(argument1, argument2)
  }
}

/**
 * Base for all types. Types are values, too.
 */
trait Type {
  /**
   * The type's supertype. A type inherits its supertype's members and must be 
   * compatible to its supertype. All types, except {@link AnyType}, must have 
   * {@link AnyType} as direct or indirect supertype.
   */
  def superTypes: Seq[Type] = Seq(AnyType)
  
  /**
   * The members of values of this type including those inherited from the supertype. 
   * The concrete member of a value is retrieved by {@link Function#apply}ing the {@link MemberDeclaration}
   * to the value. Types must override {@link #memberDeclarations} to define their 
   * direct members.
   */
  final lazy val members: Map[Symbol, MemberDeclaration] = {
    val m = memberDeclarations.toStream.map(mi => (mi.name, mi))
    val sms = superTypes.toStream map { _.members.toStream }
    HashMap.empty ++ (sms :\ m)(_ append _)
  }
  
  /**
   * Defines the direct members of this type. 
   */
  protected def memberDeclarations: Seq[MemberDeclaration]
  
  /**
   * <p>Compatibility relationship between types.</p>
   * <p>All types must conform to the following rules:
   * <ol>
   * <li>All types must be compatible to their supertype,</li>
   * <li>{@link NoType} must be compatible to all types,</li>
   * <li>All type must be compatible to themselves,</li>
   * <li>If a type <emph>A</emph> is compatible to a type 
   *     <emph>B</emph>, and <emph>B</emph> is compatible to 
   *     a type <emph>C</emph>, then <emph>A</emph> must be 
   *     compatible to <emph>C</emph>.</li>
   * </ol>
   * The first three rules are implemented in the default implementation. 
   * Subclasses, that override <code>isAssignableFrom</code>
   * should call <code>Type.isAssignableFrom</code>
   * as the final step of determing compatibility.</p>
   */
  def isAssignableFrom(other: Type, ctx: MutableTypedMap[TypedKey, Any]): Boolean = 
    other == NoType ||
    other == this ||
    (other.superTypes.exists(isAssignableFrom(_, ctx)))
  
  /**
   * Type compatibility. <code>(x >=:: y)(ctx)</code> is the same as 
   * <code>x.isAssignableFrom(y, ctx)</code>.
   */
  final def >=::(other: Type)(implicit ctx: MutableTypedMap[TypedKey, Any]): Boolean = other.isAssignableFrom(this, ctx)
  /**
   * Type incompatibility. <code>(x <:: y)(ctx)</code> is the same as 
   * <code>!x.isAssignableFrom(y, ctx)</code>.
   */
  final def <::(other: Type)(implicit ctx: MutableTypedMap[TypedKey, Any]): Boolean = !other.isAssignableFrom(this, ctx)
  /**
   * Type equality. <code>(x ==:: y)(ctx)</code> is the same as 
   * <code>x.isAssignableFrom(y, ctx) && y.isAssignableFrom(x, ctx)</code>.
   */
  final def ==::(other: Type)(implicit ctx: MutableTypedMap[TypedKey, Any]): Boolean = other.isAssignableFrom(this, ctx) && this.isAssignableFrom(other, ctx)
  /**
   * Type inequality. <code>(x !=:: y)(ctx)</code> is the same as 
   * <code>!x.isAssignableFrom(y, ctx) || !y.isAssignableFrom(x, ctx)</code>.
   */
  final def !=::(other: Type)(implicit ctx: MutableTypedMap[TypedKey, Any]): Boolean = !other.isAssignableFrom(this, ctx) || !this.isAssignableFrom(other, ctx)
  /**
   * Reversed type compatibility. <code>(x <=:: y)(ctx)</code> is the same as 
   * <code>y.isAssignableFrom(x, ctx)</code>.
   */
  final def <=::(other: Type)(implicit ctx: MutableTypedMap[TypedKey, Any]): Boolean = isAssignableFrom(other, ctx)
  /**
   * Reversed type incompatibility. <code>(x >:: y)(ctx)</code> is the same as 
   * <code>!y.isAssignableFrom(x, ctx)</code>.
   */
  final def >::(other: Type)(implicit ctx: MutableTypedMap[TypedKey, Any]): Boolean = !isAssignableFrom(other, ctx)
  
  /**
   * Check whether a certain value is actually a valid instance of this type at runtime. 
   * Mainly used for assertions. 
   */
  def isInstance(value: Any): Boolean
}

/**
 * Supertype of all types.
 */
object AnyType extends Type {
  override def toString = "any"
  override def superTypes = Seq.empty
  override def memberDeclarations = List[MemberDeclaration](
    ValueMember('toString, AnyType, TextType, _.toString),
	UnaryMember('equals, AnyType, AnyType, TruthType, (v1: Any, v2: Any) => v1 == v2)
  )
  override def isInstance(value: Any) = value != null // no nulls allowed!
}

/**
 * Type with no values.
 */
object NoType extends Type {
  override def toString = "nothing"
  override def memberDeclarations = Seq.empty 
  override def isInstance(value: Any) = false // this is the very idea of NoType
}

/**
 * Type of all types.
 */
object TypeType extends Type {
  override def toString = "type"
  override def memberDeclarations = Seq.empty
  override def isInstance(value: Any) = value.isInstanceOf[Type]
}